﻿using SuperX.Helper.Rule;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace SuperX.Helper
{
    public class FolderAndFileHelper
    {
        /// <summary>
        /// 复制文件夹及文件
        /// </summary>
        /// <param name="sourceDirPath">原文件路径</param>
        /// <param name="destFolder">目标文件路径</param>
        /// <param name="excludePaths">排除的文件夹或者文件（原文件下的相对路径）
        /// 相对路径间以英文分号“;”分隔</param>
        /// <returns></returns>
        public static string CopyFolder(string sourceDirPath, string destFolder, string excludePaths = null)
        {
            Dictionary<string, string> dicExcludePaths = new Dictionary<string, string>();
            if (!string.IsNullOrEmpty(excludePaths))
            {
                var strPaths = excludePaths.Split(';');
                if (strPaths != null && strPaths.Length > 0)
                {
                    foreach (var item in strPaths)
                    {
                        if (string.IsNullOrEmpty(item))
                        {
                            continue;
                        }

                        var newRelativePath = item.Replace(@"\", "/").Trim();
                        string fullPath = Path.Combine(sourceDirPath, newRelativePath).Replace(@"\", "/").Replace("//", "/").Trim();
                        while (fullPath.EndsWith("/"))
                        {
                            fullPath = fullPath.Remove(fullPath.Length - 1);//移除路径后面的“/”
                        }
                        if (dicExcludePaths.ContainsKey(fullPath))
                        {
                            continue;
                        }

                        dicExcludePaths.Add(fullPath, newRelativePath);
                    }
                }
            }

            return CopyFolder(sourceDirPath, destFolder, dicExcludePaths);
        }

        /// <summary>
        /// 复制文件夹及文件
        /// </summary>
        /// <param name="sourceFolder">原文件路径</param>
        /// <param name="destFolder">目标文件路径</param>
        /// <param name="dicExcludePaths">排除的文件夹或者文件（Key:源文件路径下的要排除的文件或文件夹的全路径(路径用单斜杠"/",而非"//"“\”“\\”)，否则字符串判断可能不成功,
        /// Value:目前不作要求(与key一样或者是原文件下的相对路径都行）</param>
        /// <returns></returns>
        private static string CopyFolder(string sourceFolder, string destFolder, Dictionary<string, string> dicExcludePaths = null)
        {
            try
            {
                if (string.IsNullOrEmpty(sourceFolder))
                {
                    throw new Exception("sourceFolder is null or empty");
                }

                if (string.IsNullOrEmpty(destFolder))
                {
                    throw new Exception("destFolder is null or empty");
                }

                if (!System.IO.Directory.Exists(sourceFolder))
                {
                    throw new Exception("sourceFolder is not exist");
                }

                string folderName = System.IO.Path.GetFileName(sourceFolder);
                string destfolderdir = System.IO.Path.Combine(destFolder, folderName);
                if (System.IO.Directory.Exists(destfolderdir))
                {
                    System.IO.Directory.Delete(destfolderdir, true);
                }
                System.IO.Directory.CreateDirectory(destfolderdir);
                string[] filenames = System.IO.Directory.GetFileSystemEntries(sourceFolder);
                foreach (string file in filenames)// 遍历所有的文件和目录
                {
                    string str = file.Replace(@"\", "/").Replace("//", "/");
                    if (dicExcludePaths != null && dicExcludePaths.ContainsKey(str))
                    {
                        continue;//代表此文件夹或者文件要排除，不复制
                    }

                    if (System.IO.Directory.Exists(file))
                    {
                        string currentdir = System.IO.Path.Combine(destfolderdir, System.IO.Path.GetFileName(file));
                        if (!System.IO.Directory.Exists(currentdir))
                        {
                            System.IO.Directory.CreateDirectory(currentdir);
                        }
                        CopyFolder(file, destfolderdir, dicExcludePaths);
                    }
                    else
                    {
                        string srcfileName = System.IO.Path.Combine(destfolderdir, System.IO.Path.GetFileName(file));
                        if (!System.IO.Directory.Exists(destfolderdir))
                        {
                            System.IO.Directory.CreateDirectory(destfolderdir);
                        }
                        System.IO.File.Copy(file, srcfileName, true);
                    }
                }
                return destfolderdir;
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// windows环境下连接共享文件夹（无论共享文件夹是在windows还是ubuntu）
        /// 用相应的用户名密码进行连接，连接完成后可以如同本地一样操作
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public static bool ConnectShareFolder_Windows(string ip, string userName, string password)
        {
            bool Flag = false;
            Process proc = new Process();
            try
            {
                proc.StartInfo.FileName = "cmd.exe";
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.RedirectStandardInput = true;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError = true;
                proc.StartInfo.CreateNoWindow = true;
                proc.Start();
                //拼接成：net use \\123.123.123.123 /User:username passward /PERSISTENT:NO
                string dosLine = $"net use \\\\{ip} /User:{userName} {password} /PERSISTENT:NO";//" /DELETE:YES";//+ " /PERSISTENT:YES"
                proc.StandardInput.WriteLine(dosLine);
                proc.StandardInput.WriteLine("exit");
                while (!proc.HasExited)
                {
                    proc.WaitForExit(1000);
                }
                string errormsg = proc.StandardError.ReadToEnd();
                proc.StandardError.Close();
                if (string.IsNullOrEmpty(errormsg))
                {
                    Flag = true;
                }
                else
                {
                    throw new Exception(errormsg);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                proc.Close();
                proc.Dispose();
            }
            return Flag;
        }

        /// <summary>
        /// linux环境下连接共享文件夹（无论共享文件夹是在windows还是ubuntu）
        /// 用相应的用户名密码进行连接，连接完成后可以如同本地一样操作.
        /// 本质是把共享文件夹挂在到本地
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="shareDirName">共享文件夹的名字</param>
        /// <param name="mountPath">要挂在本机Linux系统的路径</param>
        /// <returns></returns>
        public static bool ConnectShareFolder_Linux(string ip, string userName, string password, string shareDirName, string mountPath)
        {
            bool Flag = false;
            //string cmdStr = $"sudo mount -t cifs //192.168.174.223/share ~/share1 -o username='abc',password='rgt',vers=2.0";//挂载共享文件夹到本地目录下
            string cmdStr = $"sudo mount -t cifs //{ip}/{shareDirName} {mountPath} -o username='{userName}',password='{password}',vers=2.0";//挂载共享文件夹到本地目录下
            Process proc = new Process();
            try
            {
                var escapedArgs = cmdStr.Replace("\"", "\\\"");
                proc.StartInfo.FileName = "/bin/bash";
                proc.StartInfo.Arguments = $"-c \"{escapedArgs}\"";
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.RedirectStandardInput = true;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError = true;
                proc.StartInfo.CreateNoWindow = true;
                proc.Start();
                while (!proc.HasExited)
                {
                    proc.WaitForExit(1000);
                }
                string errormsg = proc.StandardError.ReadToEnd();
                proc.StandardError.Close();
                if (string.IsNullOrEmpty(errormsg))
                {
                    Flag = true;
                }
                else
                {
                    throw new Exception(errormsg);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                proc.Close();
                proc.Dispose();
            }
            return Flag;
        }
    }

    public class FilePathInfo
    {
        /// <summary>
        /// 目标文件传输位置 0：本地，1：共享文件夹， 2：FTP
        /// </summary>
        public PathType SourcePathType { get; set; }

        /// <summary>
        /// 目标文件路径
        /// </summary>
        public string SourceFileName { get; set; }

        public string SourceIP { get; set; }
        public string SourceUserName { get; set; }
        public string SourcePassward { get; set; }

        /// <summary>
        /// 文件传输位置 0：本地，1：共享文件夹， 2：FTP
        /// </summary>
        public PathType DestPathType { get; set; }

        public string DestFileName { get; set; }
        public string DestIP { get; set; }
        public string DestUserName { get; set; }
        public string DestPassward { get; set; }
        public bool Overwrite { get; set; }

        public string ErrorMsg { get; set; }

        private string _linuxTmpMountDir;
        private string _linuxTmpMountDirDest;
        private FtpWebResponse _sourceWebResponse;

        public bool HandleCopyFile()
        {
            try
            {
                if (string.IsNullOrEmpty(SourceFileName))
                {
                    ErrorMsg = $"{nameof(SourceFileName)} is null or empty";
                    return false;
                }
                if (string.IsNullOrEmpty(DestFileName))
                {
                    ErrorMsg = $"{nameof(DestFileName)} is null or empty";
                    return false;
                }
                SourceFileName = SourceFileName.Replace(@"\", "/").Trim();//转换成windows、linux通用路径
                DestFileName = DestFileName.Replace(@"\", "/").Trim();

                int buffLength = 2048;  ////
                byte[] buff = new byte[buffLength];
                int contentLen;
                using (Stream sourceSm = GetSourceStream())
                {
                    using (Stream destSm = GetDestStream())
                    {
                        try
                        {
                            // 每次读文件流的2kb
                            contentLen = sourceSm.Read(buff, 0, buffLength);
                            // 流内容没有结束
                            while (contentLen != 0)
                            {
                                // 把内容从file stream 写入 upload stream
                                destSm.Write(buff, 0, contentLen);
                                contentLen = sourceSm.Read(buff, 0, buffLength);
                            }
                        }
                        catch (Exception ex)
                        {
                            ErrorMsg = ex.Message;
                            return false;
                        }
                        finally
                        {
                            sourceSm.Close();
                            destSm.Close();
                            Close();
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                ErrorMsg = ex.Message;
                return false;
            }
        }

        public Stream GetSourceStream()
        {
            if (SourcePathType == PathType.Local)
            {
                if (!File.Exists(SourceFileName))
                {
                    return null;
                    //throw new FileNotFoundException(SourceFileName + "Can not find");
                }

                return new FileStream(SourceFileName, FileMode.Open);
            }
            else if (SourcePathType == PathType.Share)
            {
                //1.判断ip、用户名、密码是否合法
                if (!CheckIPUserNamePassward(nameof(SourceIP), nameof(SourceUserName), nameof(SourcePassward), SourceIP, SourceUserName, SourcePassward, out string str))
                {
                    ErrorMsg = str;
                    throw new Exception();
                }
                //2.连接远程
                _linuxTmpMountDir = string.Empty;//Linux下临时挂载的路径
                if (IsWindows())
                {
                    if (!FolderAndFileHelper.ConnectShareFolder_Windows(SourceIP, SourceUserName, SourcePassward))//-----------------------------------------------------------------------注意区分linux
                    {
                        ErrorMsg = $"Can not connet to {SourceIP}";
                        throw new Exception();
                    }
                }
                //3.拼接源路径
                while (SourceFileName.StartsWith("/"))
                {
                    SourceFileName = SourceFileName.Remove(0, 1);//移除路径前的“/”
                }
                if (IsWindows())
                {
                    SourceFileName = $"\\\\{SourceIP}/{SourceFileName}";//-----------------------------------------------------------------------注意区分linux
                }
                else
                {
                    SourceFileName = Path.Combine(_linuxTmpMountDir, SourceFileName);
                }
                if (!File.Exists(SourceFileName))
                {
                    throw new FileNotFoundException(SourceFileName + " can not find");
                }

                return new FileStream(SourceFileName, FileMode.Open);
            }
            else if (SourcePathType == PathType.FTP)
            {
                while (SourceFileName.StartsWith("/"))
                {
                    SourceFileName = SourceFileName.Remove(0, 1);//移除路径前的“/”
                }
                var uri = SourceIP + "/" + SourceFileName;
                FtpWebRequest reqFTP = ConnectionFTP(uri, SourceUserName, SourcePassward);
                reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
                _sourceWebResponse = (FtpWebResponse)reqFTP.GetResponse();
                return _sourceWebResponse.GetResponseStream();
            }
            else
            {
                return null;
            }
        }

        public Stream GetDestStream()
        {
            if (DestPathType == PathType.Local)
            {
                if (File.Exists(DestFileName) && !Overwrite)
                {
                    throw new Exception(DestFileName + " has exist");
                }
                string desDirPath = Path.GetDirectoryName(DestFileName);
                if (!Directory.Exists(desDirPath))
                {
                    Directory.CreateDirectory(desDirPath);
                }

                return new FileStream(DestFileName, FileMode.Create, FileAccess.ReadWrite);
            }
            else if (DestPathType == PathType.Share)
            {
                //1.连接远程
                _linuxTmpMountDirDest = string.Empty;//Linux下临时挂载的路径
                if (IsWindows())
                {
                    if (!FolderAndFileHelper.ConnectShareFolder_Windows(DestIP, DestUserName, DestPassward))//-----------------------------------------------------------------------注意区分linux
                    {
                        ErrorMsg = $"Can not connet to {DestIP}";
                        throw new Exception();
                    }
                }
                else
                {
                    while (DestFileName.StartsWith("/"))
                    {
                        DestFileName = DestFileName.Remove(0, 1);//移除路径前的“/”
                    }
                    String shareDirName = DestFileName.Split('/')[0];//原共享文件夹的名字
                    DestFileName = DestFileName.Remove(0, shareDirName.Length + 1);//去掉共享文件夹的名字，更新目的相对路径
                    //_linuxTmpMountDirDest = Path.Combine(ScadaEnvironment.OSCommand.GetSystemTmpDir(), $"IOT_{Guid.NewGuid().ToString()}");//要挂载的路径
                    //ScadaEnvironment.OSCommand.CreateMountDir(_linuxTmpMountDirDest);//创建挂载路径
                    if (!FolderAndFileHelper.ConnectShareFolder_Linux(DestIP, DestUserName, DestPassward, shareDirName, _linuxTmpMountDirDest))//连接并挂载到相应路径
                    {
                        ErrorMsg = $"Can not connet to {nameof(DestIP)}:{DestIP}";
                        throw new Exception();
                    }
                }
                //2.拼接目的路径
                while (DestFileName.StartsWith("/"))
                {
                    DestFileName = DestFileName.Remove(0, 1);//移除路径前的“/”
                }
                if (IsWindows())
                {
                    DestFileName = $"\\\\{DestIP}/{DestFileName}";//-----------------------------------------------------------------------注意区分linux
                }
                else
                {
                    DestFileName = Path.Combine(_linuxTmpMountDirDest, DestFileName);
                }
                if (File.Exists(DestFileName) && !Overwrite)
                {
                    throw new FileNotFoundException(DestFileName + " has exist");
                }

                string desDirPath = Path.GetDirectoryName(DestFileName);
                if (!Directory.Exists(desDirPath))
                {
                    Directory.CreateDirectory(desDirPath);
                }

                return new FileStream(DestFileName, FileMode.Create, FileAccess.ReadWrite);
            }
            else if (DestPathType == PathType.FTP)
            {
                while (DestFileName.StartsWith("/"))
                {
                    DestFileName = DestFileName.Remove(0, 1);//移除路径前的“/”
                }
                //检查文件夹是否存在
                if (DestFileName.Contains("/"))
                {
                    var list = DestFileName.Split('/').ToList();
                    list.RemoveAt(list.Count - 1);
                    var dirUri = DestIP + "/" + string.Join("/", list);
                    var ftp1 = ConnectionFTP(dirUri, DestUserName, DestPassward);
                    ftp1.Method = WebRequestMethods.Ftp.MakeDirectory;
                    ftp1.KeepAlive = false;
                    //异常说明文件夹已存在
                    try
                    {
                        var dirResponse = ftp1.GetResponse();
                        dirResponse.Close();
                    }
                    catch (Exception ex) { }
                }
                var uri = DestIP + "/" + DestFileName;
                //检查文件是否存在
                FtpWebRequest reqFTP1 = ConnectionFTP(uri, DestUserName, DestPassward);
                reqFTP1.Method = WebRequestMethods.Ftp.GetFileSize;
                try
                {
                    FtpWebResponse response = (FtpWebResponse)reqFTP1.GetResponse();
                    response.Close();
                    if (!Overwrite)
                    {
                        throw new Exception(DestFileName + " has exist");
                    }
                }
                catch (Exception ex) { }

                FtpWebRequest reqFTP = ConnectionFTP(uri, DestUserName, DestPassward);
                reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
                reqFTP.KeepAlive = false;
                return reqFTP.GetRequestStream();
            }
            else
            {
                return null;
            }
        }

        private FtpWebRequest ConnectionFTP(string uri, string userName, string pwd)
        {
            // 根据uri创建FtpWebRequest对象
            var reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
            // 指定数据传输类型
            reqFTP.UseBinary = true;
            // ftp用户名和密码
            reqFTP.Credentials = new NetworkCredential(userName, pwd);
            return reqFTP;
        }

        private void Close()
        {
            //if (ScadaEnvironment.IsLinux())
            //{
            //    if (!string.IsNullOrEmpty(_linuxTmpMountDir))
            //    {
            //        ScadaEnvironment.OSCommand.RemoveMountDir(_linuxTmpMountDir);//移除挂载
            //        ScadaEnvironment.OSCommand.DeleteDir(_linuxTmpMountDir);//删除临时创建的用于挂载的文件夹
            //    }
            //    if (!string.IsNullOrEmpty(_linuxTmpMountDirDest))
            //    {
            //        ScadaEnvironment.OSCommand.RemoveMountDir(_linuxTmpMountDirDest);//移除挂载
            //        ScadaEnvironment.OSCommand.DeleteDir(_linuxTmpMountDirDest);//删除临时创建的用于挂载的文件夹
            //    }
            //}
            _sourceWebResponse?.Close();
        }

        /// <summary>
        /// 检查IP、用户名、密码是否合法
        /// </summary>
        /// <param name="ipPropertyName">[IP]属性名称</param>
        /// <param name="usernamePropertyName">[用户名]属性名称</param>
        /// <param name="passwardPropertyName">[密码]属性名称</param>
        /// <param name="ip"></param>
        /// <param name="username"></param>
        /// <param name="passward"></param>
        /// <param name="msg">错误信息</param>
        /// <returns></returns>
        private bool CheckIPUserNamePassward(string ipPropertyName, string usernamePropertyName, string passwardPropertyName, string ip, string username, string passward, out string msg)
        {
            msg = string.Empty;
            if (!new IPRule().IsValidValue(ip, out msg))
            {
                msg = $"{ipPropertyName}({ip}):{msg}";
                return false;
            }
            if (string.IsNullOrEmpty(username))
            {
                msg = $"{usernamePropertyName}is null or empty";
                return false;
            }
            if (string.IsNullOrEmpty(passward))
            {
                msg = $"{passwardPropertyName}is null or empty";
                return false;
            }
            return true;
        }

        private bool IsWindows()
        {
            return true;
        }
    }

    public enum PathType
    {
        /// <summary>
        /// 本地路径
        /// </summary>
        [Description("本地路径")]
        Local,

        /// <summary>
        /// 共享文件夹路径
        /// </summary>
        [Description("共享文件夹路径")]
        Share,

        /// <summary>
        /// FTP
        /// </summary>
        [Description("FTP")]
        FTP
    }
}